﻿using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading.Tasks;
using AbpOne.Application.Caching.Authorize;
using AbpOne.Domain.Configurations;
using AbpOne.ToolKits.Base;
using AbpOne.ToolKits.Extensions;
using AbpOne.ToolKits.GitHub;
using Microsoft.IdentityModel.Tokens;

namespace AbpOne.Application.Authorize.Impl {
    public class AuthorizeService : ServiceBase, IAuthorizeService {
        /// <summary>
        /// 因为涉及到HTTP请求，在这之前我们需要在构造函数中依赖注入IHttpClientFactory，使用IHttpClientFactory创建HttpClient
        /// 使用IHttpClientFactory创建HttpClient可以自动释放对象
        /// </summary>
        private readonly IHttpClientFactory _httpClientFactory;

        private readonly IAuthorizeCacheService _authorizeCacheService;


        public AuthorizeService(IAuthorizeCacheService cacheService,
            IHttpClientFactory httpClientFactory) {
            _httpClientFactory = httpClientFactory;
            _authorizeCacheService = cacheService;
        }

        /// <summary>
        /// 获取登录地址(GitHub)
        /// </summary>
        /// <returns></returns>
        public async Task<ServiceResult<string>> GetLoginAddressAsync() {
            return await _authorizeCacheService.GetLoginAddressAsync(async () => {
                var result = new ServiceResult<string>();

                var request = new AuthorizeRequest();
                var address = string.Concat(new string[] {
                    GitHubConfig.API_Authorize,
                    "?client_id=", request.Client_ID,
                    "&scope=", request.Scope,
                    "&state=", request.State,
                    "&redirect_uri=", request.Redirect_Uri
                });

                return await Task.FromResult(result.Success(address));
            });
        }

        /// <summary>
        /// 获取AccessToken
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public async Task<ServiceResult<string>> GetAccessTokenAsync(string code) {
            var result = new ServiceResult<string>();

            if (string.IsNullOrEmpty(code)) {
                return result.Fail("code为空");
            }

            return await _authorizeCacheService.GetAccessTokenAsync(code, async () => {
                var request = new AccessTokenRequest();

                var content =
                    new StringContent(
                        $"code={code}&client_id={request.Client_ID}&redirect_uri={request.Redirect_Uri}&client_secret={request.Client_Secret}");
                content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

                using var client = _httpClientFactory.CreateClient();
                var httpResponse = await client.PostAsync(GitHubConfig.API_AccessToken, content);

                var response = await httpResponse.Content.ReadAsStringAsync();

                return response.StartsWith("access_token")
                    ? result.Success(response.Split("=")[1].Split("&").First())
                    : result.Fail("code不正确");
            });
        }

        /// <summary>
        /// 登录成功，生成Token
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public async Task<ServiceResult<string>> GenerateTokenAsync(string accessToken) {
            var result = new ServiceResult<string>();

            if (string.IsNullOrEmpty(accessToken)) {
                return result.Fail("access_token为空");
            }

            return await _authorizeCacheService.GenerateTokenAsync(accessToken, async () => {
                var url = $"{GitHubConfig.API_User}?access_token={accessToken}";
                using var client = _httpClientFactory.CreateClient();
                client.DefaultRequestHeaders.Add("User-Agent",
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.14 Safari/537.36 Edg/83.0.478.13");
                var httpResponse = await client.GetAsync(url);
                if (httpResponse.StatusCode != HttpStatusCode.OK) {
                    return result.Fail("access_token不正确");
                }

                var content = await httpResponse.Content.ReadAsStringAsync();

                var user = content.FromJson<UserResponse>();
                if (user.IsNull()) {
                    return result.Fail("未获取到用户数据");
                }

                if (user.Id != GitHubConfig.UserId) {
                    return result.Fail("当前账号未授权");
                }

                var claims = new[] {
                    new Claim(ClaimTypes.Name, user.Name),
                    new Claim(ClaimTypes.Email, user.Email),
                    new Claim(JwtRegisteredClaimNames.Exp,
                        $"{new DateTimeOffset(DateTime.Now.AddMinutes(AppSettings.JWT.Expires)).ToUnixTimeSeconds()}"),
                    new Claim(JwtRegisteredClaimNames.Nbf, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}")
                };

                var key = new SymmetricSecurityKey(AppSettings.JWT.SecurityKey.SerializeUtf8());
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                var securityToken = new JwtSecurityToken(
                    issuer: AppSettings.JWT.Domain,
                    audience: AppSettings.JWT.Domain,
                    claims: claims,
                    expires: DateTime.Now.AddMinutes(AppSettings.JWT.Expires),
                    signingCredentials: creds);

                var token = new JwtSecurityTokenHandler().WriteToken(securityToken);

                return await Task.FromResult(result.Success(token));
            });
        }
    }
}